#include "apic.h"
#include "cpu.h"
#include "printk.h"
#include "interrupt.h"
#include "lib.h"
#include "memory.h"

st_io_apic_map g_ioapic_map;

void local_apic_init() {
    unsigned int x, y;
    unsigned int a, b, c, d;

    // g_global_cr3 = get_gdt();
    // unsigned long *table1 = *(Phy_To_Virt(g_global_cr3) + 0x100) & (~0xfffUL);
    // unsigned long *table2 = *(Phy_To_Virt(table1) + 0x3) & (~0xfffUL);
    // unsigned long *table3 = *(Phy_To_Virt(table2) + 0x1f7);
    // color_printk(INDIGO, BLACK, "debug g_global_cr3\t:%#018lx\n", g_global_cr3);
    // color_printk(INDIGO, BLACK, "debug g_global_cr3\t:%#018lx\n", table1);
    // color_printk(INDIGO, BLACK, "debug *g_global_cr3\t:%#018lx\n", table2);
    // color_printk(PURPLE, BLACK, "debug **g_global_cr3\t:%#018lx\n", table3);
    // return;

    // 检查是否支持APIC模式和x2APIC模式
    get_cpuid(1, 0, &a, &b, &c, &d);
    color_printk(WHITE, BLACK, "CPUID\t01, eax:%#010x, ebx:%#010x, ecx:%#010x, edx:%#010x\n", a, b, c, d);
    if ((1 << 9) & d) {
        color_printk(WHITE, BLACK, "HW support APIC&xAPIC\t");
    } else {
        color_printk(WHITE, BLACK, "HW NO support APIC&xAPIC\t");
    }

    if ((1 << 21) & c) {
        color_printk(WHITE, BLACK, "HW support x2APIC\n");
    } else {
        color_printk(WHITE, BLACK, "HW NO support x2APIC\n");
    }

    // 通过设置IA32_APIC_BASE寄存器的第10位，第11位来开启Local APIC的xAPIC&x2APIC模式
    __asm__ __volatile__(
        "movq $0x1b, %%rcx \n\t"
        "rdmsr \n\t"
        "bts $10, %%rax \n\t"
        "bts $11, %%rax \n\t"
        "wrmsr \n\t"
        "movq $0x1b, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(WHITE, BLACK, "eax:%#010x, edx:%#010x\t", x, y);
    if (x & 0xc00) {    // 读取寄存器，检查是否正常开启
        color_printk(WHITE, BLACK, "xAPIC & x2APIC enabled\n");
    }

    // 设置SVR寄存器第8位、第12位开启Local APIC和禁止广播EOI消息
    __asm__ __volatile__(
        "movq $0x80f, %%rcx \n\t"
        "rdmsr \n\t"
        "bts $8, %%rax \n\t"
        //"bts $12, %%rax \n\t"         // 我的电脑设置bit12会报general_protection错误
        "wrmsr \n\t"
        "movq $0x80f, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(WHITE, BLACK, "eax:%#010x, edx:%#010x\t", x, y);
    if (x & 0x100) {
        color_printk(WHITE, BLACK, "SVR[8] enabled\n");
    }
    if (x & 0x1000) {
        color_printk(WHITE, BLACK, "SVR[12] enabled\n");
    }

    // 通过MSR寄存器组的0x802和0x803寄存器获取APIC ID和版本信息
    __asm__ __volatile__(
        "movq $0x802, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(WHITE, BLACK, "eax:%#010x, edx:%#010x\tx2APIC ID:%#010x\n", x, y, x);
    __asm__ __volatile__(
        "movq $0x803, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(WHITE, BLACK, "Local APIC version:%#010x, max LVT entry:%#010x, SVR(Suppress EOI Broadcast):%#04x\t", x % 0xff, (x >> 16 & 0xff) + 1, x >> 24 & 0x1);
    if ((x & 0xff) < 0x10) {
        color_printk(WHITE, BLACK, "82489DX discrete APIC\n");
    } else if (((x & 0xff) >= 0x10) && ((x & 0xff) <= 0x15)) {
        color_printk(WHITE, BLACK, "Integrated APIC\n");
    }

    // LVT处理程序还没准备好，先屏蔽LVT中所有中断投递功能
    __asm__ __volatile__(
        // "movq $0x82f, %%rcx \n\t"       // CMCI
        // "wrmsr \n\t"
        "movq $0x832, %%rcx \n\t"       // Timer
        "wrmsr \n\t"
        "movq $0x833, %%rcx \n\t"       // Thermal Monitor
        "wrmsr \n\t"
        "movq $0x834, %%rcx \n\t"       // Performance Counter
        "wrmsr \n\t"
        "movq $0x835, %%rcx \n\t"       // LINT0
        "wrmsr \n\t"
        "movq $0x836, %%rcx \n\t"       // LINT1
        "wrmsr \n\t"
        "movq $0x837, %%rcx \n\t"       // Error
        "wrmsr \n\t"
        :
        :"a"(0x10000), "d"(0x00)
        :"memory"
    );
    color_printk(GREEN, BLACK, "mask all LVT\n");

    // 获取TPR寄存器值
    __asm__ __volatile__(
        "movq $0x808, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(GREEN, BLACK, "Set LVT TPR:%#010x\t", x);

    // 获取PPR寄存器值
    __asm__ __volatile__(
        "movq $0x80a, %%rcx \n\t"
        "rdmsr \n\t"
        :"=a"(x), "=d"(y)
        :
        :"memory"
    );
    color_printk(GREEN, BLACK, "Set LVT PPR:%#010x\n", x);
}

void apic_ioapic_init() {
    int i;
    unsigned int x;
    unsigned int *p;
    unsigned long apic_bar = 0;

    io_apic_pagetable_remap();

    for (i = 32; i < 56; ++i) {
        set_intr_gate(i, 2, g_interrupt[i - 32]);
    }

    // 屏蔽8259A
    color_printk(GREEN, BLACK, "mask 8259A\n");
    io_out8(0x21, 0xff);
    io_out8(0xa1, 0xff);

    // enable IMCR
    io_out8(0x22, 0x70);
    io_out8(0x23, 0x01);

    local_apic_init();

    ioapic_init();

    // 以下代码在virtual box虚拟机上不起作用，会报page异常
    // // get RCBA address，位于PCI总线0的第31号LPC桥控制器组的0号功能的F0h偏移地址处，PCI设备地址0x80000000，0x80000000 | (0 << 16) | (31 << 11) | (0 << 8) | (0xf0 & 0xfc) = 0x8000f8f0
    // io_out32(0xcf8, 0x8000f8f0);
    // x = io_in32(0xcfc);
    // color_printk(RED, BLACK, "get RCBA address:%#010x\n", x);
    // x = x & 0xffffc000;
    // color_printk(RED, BLACK, "get RCBA address:%#010x\n", x);

    // // get OIC address
    // if (x > 0xfec00000 && x < 0xfee00000) {
    //     p = (unsigned int *)Phy_To_Virt(x + 0x31feUL);
    // }

    // // enable I/O APIC
    // x = (*p & 0xffffff00) | 0x100;
    // IO_MFENCE();
    // *p = x;
    // IO_MFENCE();

    memset(g_intr_desc, 0, sizeof(st_irq_desc) * NR_IRQS);

    STI();
}

// rdi传递参数regs, rsi传递参数nr
void do_irq(st_pt_regs *regs, unsigned long nr) {
    st_irq_desc *irq = &g_intr_desc[nr - 32];

    if (irq->handler) {
        irq->handler(nr, irq->param, regs);
    }
    if (irq->controller && irq->controller->ack) {
        irq->controller->ack(nr);
    }
}

// I/O APIC内存地址需要映射到线性地址空间才能访问
void io_apic_pagetable_remap() {
    unsigned long *tmp;
    unsigned char *io_apic_addr = (unsigned char *)Phy_To_Virt(0xfec00000);
    g_ioapic_map.phy_addr = 0xfec00000;
    g_ioapic_map.vir_index_addr = io_apic_addr;
    g_ioapic_map.vir_data_addr = (unsigned int *)(io_apic_addr + 0x10);
    g_ioapic_map.vir_eoi_addr = (unsigned int *)(io_apic_addr + 0x40);
    
    g_global_cr3 = get_gdt();
    tmp = Phy_To_Virt(g_global_cr3 + (((unsigned long)io_apic_addr >> PAGE_GDT_SHIFT) & 0x1ff));
    if (*tmp == 0) {    // 没有映射物理内存，申请一个内存页作为次级表，并将次级表物理地址设置到GDT表项中
        unsigned long *virtual = kmalloc(PAGE_4K_SIZE, 0);
        SET_MPL4T(tmp, MK_MPL4T(Virt_To_Phy(virtual), PAGE_KERNEL_GDT));
    }
    color_printk(YELLOW, BLACK, "1:%#018lx\t%#018lx\n", (unsigned long)tmp, (unsigned long)*tmp);
    tmp = Phy_To_Virt((unsigned long *)(*tmp & (~0xfffUL)) + (((unsigned long)io_apic_addr >> PAGE_1G_SHIFT) & 0x1ff));
    if (*tmp == 0) {
        unsigned long *virtual = kmalloc(PAGE_4K_SIZE, 0);
        SET_PDPT(tmp, MK_PDPT(Virt_To_Phy(virtual), PAGE_KERNEL_DIR));
    }
    color_printk(YELLOW, BLACK, "2:%#018lx\t%#018lx\n", (unsigned long)tmp, (unsigned long)*tmp);
    tmp = Phy_To_Virt((unsigned long *)(*tmp & (~0xfffUL)) + (((unsigned long)io_apic_addr >> PAGE_2M_SHIFT) & 0x1ff));
    SET_PDT(tmp, MK_PDT(g_ioapic_map.phy_addr, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD));
    color_printk(BLUE, BLACK, "3:%#018lx\t%#018lx\n", (unsigned long)tmp, (unsigned long)*tmp);
    color_printk(BLUE, BLACK, "g_ioapic_map.phy_addr:%#010lx\t\t\n", g_ioapic_map.phy_addr);
    color_printk(BLUE, BLACK, "g_ioapic_map.vir_addr:%#018lx\t\t\n", g_ioapic_map.vir_index_addr);
    FLUSH_TLB();
}

unsigned long ioapic_rte_read(unsigned char index) {
    unsigned long ret;

    // rte寄存器为64位，间接访问寄存器只有32位，因此要连续访问两次
    *g_ioapic_map.vir_index_addr = index + 1;
    IO_MFENCE();    // 强制处理器串行化执行，确保当前指令前的读写操作全部完成
    ret = *g_ioapic_map.vir_data_addr;
    ret <<= 32;
    IO_MFENCE();

    *g_ioapic_map.vir_index_addr = index;
    IO_MFENCE();
    ret |= *g_ioapic_map.vir_data_addr;
    IO_MFENCE();
    return ret;
}

void ioapic_rte_write(unsigned char index, unsigned long value) {
    *g_ioapic_map.vir_index_addr = index;
    IO_MFENCE();
    *g_ioapic_map.vir_data_addr = value & 0xffffffff;
    value >>= 32;
    IO_MFENCE();
    *g_ioapic_map.vir_index_addr = index + 1;
    IO_MFENCE();
    *g_ioapic_map.vir_data_addr = value & 0xffffffff;
    IO_MFENCE();
}

void ioapic_init() {
    int i;
    *g_ioapic_map.vir_index_addr = 0x00;        // I/O APIC ID寄存器
    IO_MFENCE();
    *g_ioapic_map.vir_data_addr = 0x0f000000;
    IO_MFENCE();
    color_printk(GREEN, BLACK, "get I/O APIC ID reg:%#010x, ID:%#010X\n", *g_ioapic_map.vir_data_addr, *g_ioapic_map.vir_data_addr >> 24 & 0xf);
    IO_MFENCE();

    *g_ioapic_map.vir_index_addr = 0x01;
    IO_MFENCE();
    color_printk(GREEN, BLACK, "get I/O APIC version reg:%#010x, max redirection entries:%#08d\n", *g_ioapic_map.vir_data_addr, ((*g_ioapic_map.vir_data_addr >> 16) & 0xff) + 1);

    // 先屏蔽所有中断
    for (i = 0x10; i < 0x40; i += 2) {
        ioapic_rte_write(i, 0x10020 + ((i - 0x10) >> 1));       // 0x10020中1表示屏蔽该中断，0x20表示起始中断号，0~31为系统使用
    }
    color_printk(GREEN, BLACK, "I/O APIC redirection table entries set finished.\n");
}

void ioapic_enable(unsigned long irq) {
    unsigned long value = 0;
    value = ioapic_rte_read(GET_IOAPIC_RTE_INDEX(irq));  
    value = value & (~0x10000UL);   // 复位屏蔽标志位
    ioapic_rte_write(GET_IOAPIC_RTE_INDEX(irq), value);
}

void ioapic_disable(unsigned long irq) {
    unsigned long value = 0;
    value = ioapic_rte_read(GET_IOAPIC_RTE_INDEX(irq));  // 第一个中断定向投递寄存器偏移地址为0x10
    value = value | 0x10000UL;   // 置位屏蔽标志位
    ioapic_rte_write(GET_IOAPIC_RTE_INDEX(irq), value);
}

unsigned long ioapic_install(unsigned long irq, void *arg) {
    st_io_apic_rte_entry *entry = (st_io_apic_rte_entry *)arg;
    ioapic_rte_write(GET_IOAPIC_RTE_INDEX(irq), *(unsigned long *)entry);
}

void ioapic_uninstall(unsigned long irq) {
    ioapic_rte_write(GET_IOAPIC_RTE_INDEX(irq), 0x10000UL); // 除了屏蔽标志位，其他设置为0
}

void ioapic_level_ack(unsigned long irq) {
    // 写EOI寄存器为00，通知控制器中断处理过程结束
    __asm__ __volatile__(
        "movq $0x00, %%rdx \n\t"
        "movq $0x00, %%rax \n\t"
        "movq $0x80b, %%rcx \n\t"
        "wrmsr \n\t"
        :::"memory"
    );
    *g_ioapic_map.vir_eoi_addr = irq;
}

void ioapic_edge_ack(unsigned long irq) {
    // 写EOI寄存器为00，通知控制器中断处理过程结束
    __asm__ __volatile__(
        "movq $0x00, %%rdx \n\t"
        "movq $0x00, %%rax \n\t"
        "movq $0x80b, %%rcx \n\t"
        "wrmsr \n\t"
        :::"memory"
    );
}